/**********************************************************************
 *<
	FILE: vertex_tree_paint.cpp

	DESCRIPTION:	Modifier implementation	

	CREATED BY: Christer Janson, Nikolai Sander

	HISTORY: 

 *>	Copyright (c) 1997, All Rights Reserved.
 **********************************************************************/

#include "vertex_tree_paint.h"
#include "meshdelta.h"

// flags:
#define VP_DISP_END_RESULT 0x01

static WNDPROC colorSwatchOriginalWndProc;

static	HIMAGELIST hButtonImages = NULL;

static void LoadImages() {
	if (hButtonImages) return;
	HBITMAP hBitmap, hMask;
	hButtonImages = ImageList_Create(15, 14, ILC_MASK, 2, 0);	// 17 is kluge to center square. -SA
	hBitmap     = LoadBitmap (hInstance,MAKEINTRESOURCE(IDB_BUTTONS));
	hMask       = LoadBitmap (hInstance,MAKEINTRESOURCE(IDB_BUTTON_MASK));
	ImageList_Add(hButtonImages, hBitmap, hMask);
	DeleteObject(hBitmap);
	DeleteObject(hMask);
}

ClassDesc* GetVertexPaintDesc();


class VertexPaintClassDesc:public ClassDesc {
	public:
	int 			IsPublic()					{return 1;}
	void *			Create(BOOL loading = FALSE){return new VertexPaint();}
	const TCHAR *	ClassName()					{return GetString(IDS_CLASS_NAME);}
	SClass_ID		SuperClassID()				{return OSM_CLASS_ID;}
	Class_ID		ClassID()					{return VERTEX_TREE_PAINT_CLASS_ID;}
	const TCHAR* 	Category()					{return GetString(IDS_CATEGORY);}
	void			ResetClassParams(BOOL fileReset) {}
	};

static VertexPaintClassDesc VertexPaintDesc;
ClassDesc* GetVertexPaintDesc() {return &VertexPaintDesc;}

static INT_PTR CALLBACK VertexPaintDlgProc(
		HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
	int numPoints;
	VertexPaint *mod = (VertexPaint*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
	if (!mod && msg!=WM_INITDIALOG) return FALSE;
	int		comboResult;

	
	// Manages Spinners.
	if (((msg==CC_SPINNER_BUTTONUP) && HIWORD(wParam)) ||
		((msg==CC_SPINNER_CHANGE) ))
	{
		ISpinnerControl *spin;
		spin = (ISpinnerControl *) lParam;
		
		switch (LOWORD(wParam)) 
		{
		case IDC_TINT_SPIN:
			if ((msg == CC_SPINNER_CHANGE))
			{
				mod->fTint = spin->GetFVal()/100;
			}
			break;
		case IDC_BEND_SPIN:
			if ((msg == CC_SPINNER_CHANGE))
			{
				mod->fGradientBend = spin->GetFVal()/100;
			}
			break;
		}
	}

	switch (msg) {
		case WM_INITDIALOG:
			LoadImages();
			mod = (VertexPaint*)lParam;
			SetWindowLongPtr(hWnd,GWLP_USERDATA,lParam);
			mod->hParams = hWnd;
			mod->iPaintButton = GetICustButton(GetDlgItem(hWnd, IDC_PAINT));
			mod->iPaintButton->SetType(CBT_CHECK);
			mod->iPaintButton->SetHighlightColor(GREEN_WASH);
			mod->iPaintButton->SetCheck(mod->ip->GetCommandMode()->ID() == CID_PAINT && 
				!((PaintMouseProc *)mod->ip->GetCommandMode()->MouseProc(&numPoints))->GetPickMode());
			mod->iPaintButton->SetImage(hButtonImages,0,0,0,0,15,14);
			mod->iPaintButton->SetTooltip (TRUE, GetString (IDS_PAINT));

			mod->iPickButton = GetICustButton(GetDlgItem(hWnd, IDC_PICK));
			mod->iPickButton->SetType(CBT_CHECK);
			mod->iPickButton->SetHighlightColor(GREEN_WASH);
			mod->iPickButton->SetCheck(mod->ip->GetCommandMode()->ID() == CID_PAINT && 
				((PaintMouseProc *)mod->ip->GetCommandMode()->MouseProc(&numPoints))->GetPickMode());
			mod->iPickButton->SetImage(hButtonImages,1,1,1,1,15,14);
			mod->iPickButton->SetTooltip (TRUE, GetString (IDS_PICK));


			mod->iColor = GetIColorSwatch(GetDlgItem(hWnd, IDC_COLOR));
			// change current Color according to editMode
			mod->reloadBkupColor();

			// Get interface For ZGradient, reload bkuped colors
			mod->iColorGradient[0] = GetIColorSwatch(GetDlgItem(hWnd, IDC_PALETTE_GRAD0));
			mod->iColorGradient[1] = GetIColorSwatch(GetDlgItem(hWnd, IDC_PALETTE_GRAD1));
			mod->iColorGradient[0]->SetColor(mod->lastGradientColor[0]);
			mod->iColorGradient[1]->SetColor(mod->lastGradientColor[1]);

			
			// Init comboBox
			SendDlgItemMessage(hWnd, IDC_COMBO_TYPE, CB_ADDSTRING, 0, (LPARAM)"Tree Weight");
			SendDlgItemMessage(hWnd, IDC_COMBO_TYPE, CB_ADDSTRING, 0, (LPARAM)"Phase Level 1");
			SendDlgItemMessage(hWnd, IDC_COMBO_TYPE, CB_ADDSTRING, 0, (LPARAM)"Phase Level 2");
			SendDlgItemMessage(hWnd, IDC_COMBO_TYPE, CB_SETCURSEL, mod->getEditionType(), 0);

			// If paint mode at last edit.
			if(mod->_LastPaintMode)
			{
				// ActivatePaint / check button.
				mod->ActivatePaint(TRUE);
				mod->iPaintButton->SetCheck(TRUE);
			}

			break;

		case WM_POSTINIT:
			mod->InitPalettes();
			break;

		case CC_COLOR_CHANGE:
			if (LOWORD(wParam) == IDC_COLOR) 
			{
				IColorSwatch* iCol = (IColorSwatch*)lParam;
				switch(mod->getEditionType())
				{
				case 0: mod->lastWeightColor = iCol->GetColor(); break;
				case 1:
				case 2: 
					mod->lastPhaseColor = iCol->GetColor(); break;
				}
			}
			break;
		case WM_DESTROY:
			mod->SavePalettes();
			mod->iPaintButton = NULL;
			mod->iPickButton = NULL;
			mod->iColor = NULL;
			mod->iColorGradient[0] = NULL;
			mod->iColorGradient[1] = NULL;
			break;

		case WM_COMMAND:
			switch(LOWORD(wParam)) {
				case IDC_PAINT:
					mod->ActivatePaint(mod->iPaintButton->IsChecked());
					break;
				case IDC_PICK:
					mod->ActivatePaint(mod->iPickButton->IsChecked(),TRUE);
					break;

				case IDC_VC_ON:
					mod->TurnVCOn(FALSE);
					break;
				case IDC_SHADED:
					mod->TurnVCOn(TRUE);
					break;
				case IDC_COMBO_TYPE:
					// Init default type.
					comboResult= SendDlgItemMessage(hWnd, IDC_COMBO_TYPE, CB_GETCURSEL, 0, 0);
					mod->setEditionType(comboResult);
					break;
				case IDC_BUTTON_FILL:
					mod->fillSelectionColor();
					break;
				case IDC_BUTTON_GRADIENT:
					mod->fillSelectionGradientColor();
					break;
				case IDC_BUTTON_GRAD0:
					mod->iColorGradient[0]->SetColor(RGB(0,0,0));
					mod->iColorGradient[1]->SetColor(RGB(85,85,85));
					break;
				case IDC_BUTTON_GRAD1:
					mod->iColorGradient[0]->SetColor(RGB(85,85,85));
					mod->iColorGradient[1]->SetColor(RGB(170,170,170));
					break;
				case IDC_BUTTON_GRAD2:
					mod->iColorGradient[0]->SetColor(RGB(170,170,170));
					mod->iColorGradient[1]->SetColor(RGB(255,255,255));
					break;
				}
			break;

		default:
			return FALSE;
		}
	return TRUE;
	}

// Subclass procedure 
LRESULT APIENTRY colorSwatchSubclassWndProc(
    HWND hwnd, 
    UINT uMsg, 
    WPARAM wParam, 
    LPARAM lParam) 
{
	switch (uMsg) {
		case WM_LBUTTONDOWN:
		case WM_LBUTTONUP:
		case WM_LBUTTONDBLCLK: {
			HWND hPanel = GetParent(hwnd);
			LONG_PTR mod = GetWindowLongPtr(hPanel,GWLP_USERDATA);
			if (mod) {
				((VertexPaint*)mod)->PaletteButton(hwnd);
				}
			}
			break;
		case WM_DESTROY:
			SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) colorSwatchOriginalWndProc); 
			// Fallthrough...
		default:
			return CallWindowProc(colorSwatchOriginalWndProc, hwnd, uMsg, wParam, lParam); 
			break;
		}
	return 0;
	}
 

IObjParam *VertexPaint::ip			= NULL;
HWND VertexPaint::hParams			= NULL;		
VertexPaint* VertexPaint::editMod	= NULL;
ICustButton* VertexPaint::iPaintButton	= NULL;
ICustButton* VertexPaint::iPickButton	= NULL;
IColorSwatch* VertexPaint::iColor	= NULL;
COLORREF VertexPaint::lastWeightColor		= RGB(85,85,85);
COLORREF VertexPaint::lastPhaseColor		= RGB(0,0,0);
COLORREF VertexPaint::palColors[]	= {
	//RGB(32,  32,  32),	RGB(  96,96,96),	RGB(  160,160,160),	RGB(224,224,224) };
	RGB(0,  0,  0),	RGB(  85,85,85),	RGB(  170,170,170),	RGB(255,255,255),
		 RGB(42,  42,  42), RGB(  127, 127, 127), RGB(  212, 212, 212)};


IColorSwatch* VertexPaint::iColorGradient[]= {NULL, NULL};
COLORREF VertexPaint::lastGradientColor[] = {RGB(0,  0,  0), RGB(85,  85,  85)};


//--- VertexPaint -------------------------------------------------------
VertexPaint::VertexPaint() : iTint(NULL), fTint(1.0f), iGradientBend(NULL), fGradientBend(0.0f)
	{
	flags = 0x0;
	_EditType= 0;
	_LastPaintMode= false;
	}

VertexPaint::~VertexPaint()
	{
	}

Interval VertexPaint::LocalValidity(TimeValue t)
	{
	return FOREVER;
	}

BOOL VertexPaint::DependOnTopology(ModContext &mc)
	{
	return TRUE;
	}

RefTargetHandle VertexPaint::Clone(RemapDir& remap)
	{
	VertexPaint* newmod = new VertexPaint();	
	return(newmod);
	}

void VertexPaint::NotifyInputChanged(Interval changeInt, PartID partID, RefMessage message, ModContext *mc)
	{
	if (!mc->localData) return;
	((VertexPaintData*)mc->localData)->FreeCache();

	}

void VertexPaint::ModifyObject(TimeValue t, ModContext &mc, ObjectState * os, INode *node) 
	{
	if (!os->obj->IsSubClassOf(triObjectClassID)) return;
	
	os->obj->ReadyChannelsForMod(GEOM_CHANNEL|TOPO_CHANNEL|VERTCOLOR_CHANNEL|TEXMAP_CHANNEL);
	
	TriObject *tobj = (TriObject*)os->obj;
	VertexPaintData *d  = (VertexPaintData*)mc.localData;
		
	Mesh* mesh = &tobj->GetMesh();
	
	if (mesh) 
	{
		// We don't have any VColors yet, so we allocate the vcfaces
		// and set all vcolors to black (index 0)

		if (!mesh->vcFace) 
		{
			mesh->setNumVCFaces(mesh->getNumFaces());
			mesh->setNumVertCol(1);
			
			mesh->vertCol[0] = Color(0,0,0);

			for (int f=0; f<mesh->getNumFaces(); f++) 
			{
				mesh->vcFace[f].t[0] = 0;
				mesh->vcFace[f].t[1] = 0;
				mesh->vcFace[f].t[2] = 0;
			}
		}

		if (!d) mc.localData = d = new VertexPaintData(tobj->GetMesh());	
		if (!d->GetMesh()) d->SetCache(*mesh);	

		
		{
			MeshDelta md(*mesh);
			//MeshDelta mdc;
			//if(cache) mdc.InitToMesh(*cache);

			// If the incoming Mesh had no vertex colors, this will add a default map to start with.
			// The default map has the same topology as the Mesh (so one color per vertex),
			// with all colors set to white.
			if (!mesh->mapSupport(0)) md.AddVertexColors ();
			//if (cache && !cache->mapSupport(0)) mdc.AddVertexColors ();
			
			// We used two routines -- VCreate to add new map vertices, and FRemap to make the
			// existing map faces use the new verts.  frFlags tell FRemap which vertices on a face
			// should be "remapped", and the ww array contains the new locations.			
			VertColor nvc;
			int j;
			for (int v=0; v < d->GetNumColors(); v++) 
			{
				ColorData cd = d->GetColorData(v);
			
				// Edition Mode ??
				if(editMod == this)
				{
					nvc= Color(cd.color);
					// change color to view only monochromatic info for this channel;
					switch(_EditType)
					{
					case 0: nvc.y= nvc.z= nvc.x; 
						nvc.y*= 0.7f;
						nvc.z*= 0.7f;
						break;
					case 1: nvc.x= nvc.z= nvc.y; 
						nvc.x*= 0.7f;
						nvc.z*= 0.7f;
						break;
					case 2: nvc.x= nvc.y= nvc.z; 
						nvc.x*= 0.7f;
						nvc.y*= 0.7f;
						break;
					}
				}
				else
				{
					// replace the VertexColor of the outgoing mesh
					nvc= Color(cd.color);
				}

				DWORD ww[3], frFlags;
				
				md.map->VCreate (&nvc);
				
				// increase the number of vcol's and set the vcfaces as well	
				for(int i = 0 ; i < d->GetNVert(v).faces.Count() ; i++)
				{		
					j = d->GetNVert(v).whichVertex[i];
					frFlags = (1<<j);
					ww[j] = md.map->outVNum()-1;
					md.map->FRemap(d->GetNVert(v).faces[i], frFlags, ww);
					
				}
			} 

			md.Apply(*mesh);
		}

	
		NotifyDependents(FOREVER, PART_VERTCOLOR, REFMSG_CHANGE);
		os->obj->UpdateValidity(VERT_COLOR_CHAN_NUM, Interval(t,t));
	}
}

static bool oldShowEnd;

void VertexPaint::BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev )
	{
	
	this->ip = ip;
	editMod = this;
	if (!hParams) {
		hParams = ip->AddRollupPage( 
				hInstance, 
				MAKEINTRESOURCE(IDD_PANEL),
				VertexPaintDlgProc, 
				GetString(IDS_PARAMS), 
				(LPARAM)this);

		// Subclass the palette controls
		hPaletteWnd[ 0] = GetDlgItem(hParams, IDC_PALETTE_1);
		hPaletteWnd[ 1] = GetDlgItem(hParams, IDC_PALETTE_2);
		hPaletteWnd[ 2] = GetDlgItem(hParams, IDC_PALETTE_3);
		hPaletteWnd[ 3] = GetDlgItem(hParams, IDC_PALETTE_4);
		hPaletteWnd[ 4] = GetDlgItem(hParams, IDC_PALETTE_5);
		hPaletteWnd[ 5] = GetDlgItem(hParams, IDC_PALETTE_6);
		hPaletteWnd[ 6] = GetDlgItem(hParams, IDC_PALETTE_7);

		int	i;
		for (i=0; i<NUMPALETTES; i++) {
			colorSwatchOriginalWndProc = (WNDPROC) SetWindowLongPtr(hPaletteWnd[i], GWLP_WNDPROC, (LONG_PTR) colorSwatchSubclassWndProc); 
			}

		SendMessage(hParams, WM_POSTINIT, 0, 0);
		}
	else
	{
		SetWindowLongPtr(hParams,GWLP_USERDATA,(LONG_PTR)this);
	}

	iTint = SetupIntSpinner (hParams, IDC_TINT_SPIN, IDC_TINT, 0, 100, (int) (fTint*100.0f));

	// Init Gradient Bend spinner
	iGradientBend = SetupIntSpinner (hParams, IDC_BEND_SPIN, IDC_BEND, 0, 100, (int) (fGradientBend*100.0f));

	
	// Set show end result.
	oldShowEnd = ip->GetShowEndResult() ? TRUE : FALSE;
	ip->SetShowEndResult (GetFlag (VP_DISP_END_RESULT));

	// Force an eval to update caches.
	NotifyDependents(FOREVER, PART_VERTCOLOR, REFMSG_CHANGE);
}

void VertexPaint::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next)
	{
	// Dsiable Painting.
	bool	lpm= _LastPaintMode;
	ActivatePaint(FALSE);
	// bkup lastPainMode
	_LastPaintMode= lpm;
	
	ReleaseISpinner (iTint);
	ReleaseISpinner (iGradientBend);
	
	ModContextList list;
	INodeTab nodes;
	ip->GetModContexts(list,nodes);
	for (int i=0; i<list.Count(); i++) {
		VertexPaintData *vd = (VertexPaintData*)list[i]->localData;
		if (vd) vd->FreeCache();
	}
	nodes.DisposeTemporary();

	// Reset show end result
	SetFlag (VP_DISP_END_RESULT, ip->GetShowEndResult() ? TRUE : FALSE);
	ip->SetShowEndResult(oldShowEnd);


	// Exit editMod => draw true colored weights.
	editMod = NULL;
	NotifyDependents(FOREVER, PART_VERTCOLOR, REFMSG_CHANGE);


	ip->DeleteRollupPage(hParams);
	hParams = NULL;
	iTint = NULL;
	iGradientBend= NULL;
	this->ip = NULL;
	}


//From ReferenceMaker 
RefResult VertexPaint::NotifyRefChanged(
		Interval changeInt, RefTargetHandle hTarget,
		PartID& partID,  RefMessage message) 
	{
	return REF_SUCCEED;
	}

int VertexPaint::NumRefs() 
	{
	return 0;
	}

RefTargetHandle VertexPaint::GetReference(int i) 
	{
	return NULL;
	}

void VertexPaint::SetReference(int i, RefTargetHandle rtarg)
	{
	}

int VertexPaint::NumSubs() 
	{ 
	return 0;
	}  

Animatable* VertexPaint::SubAnim(int i) 
	{ 
	return NULL; 
	}

TSTR VertexPaint::SubAnimName(int i) 
	{ 
	return _T("");
	}


#define VERSION_CHUNKID			0x100
#define COLORLIST_CHUNKID		0x120

static int currentVersion = 1;

IOResult VertexPaint::Load(ILoad *iload)
	{
	IOResult res;
	ULONG nb;
	int version = 1;
	Modifier::Load(iload);

	while (IO_OK==(res=iload->OpenChunk())) {
		switch(iload->CurChunkID())  {
			case VERSION_CHUNKID:
				iload->Read (&version, sizeof(version), &nb);
				break;
			}
		iload->CloseChunk();
		if (res!=IO_OK) return res;
		}

	return IO_OK;
	}

IOResult VertexPaint::Save(ISave *isave)
	{
	IOResult res;
	ULONG nb;

	Modifier::Save(isave);

	isave->BeginChunk(VERSION_CHUNKID);
	res = isave->Write (&currentVersion, sizeof(int), &nb);
	isave->EndChunk();

	return IO_OK;
	}

IOResult VertexPaint::SaveLocalData(ISave *isave, LocalModData *ld)
	{
	VertexPaintData*	d = (VertexPaintData*)ld;
	IOResult	res;
	ULONG		nb;
	int			numColors;
	ColorData	col;
	
	isave->BeginChunk(VERSION_CHUNKID);
	res = isave->Write (&currentVersion, sizeof(int), &nb);
	isave->EndChunk();
	
	isave->BeginChunk(COLORLIST_CHUNKID);
	numColors = d->GetNumColors();
	res = isave->Write(&numColors, sizeof(int), &nb);
	for (int i=0; i<numColors ; i++) {
		col = d->GetColorData(i);
		isave->Write(&col.color,sizeof(col.color),&nb);
		}

	isave->EndChunk();
	return IO_OK;
	}

IOResult VertexPaint::LoadLocalData(ILoad *iload, LocalModData **pld) {
	VertexPaintData *d = new VertexPaintData;
	IOResult	res;	
	ULONG		nb;
	int			version = 1;
	int			numColors;
	ColorData	col;

	*pld = d;

	while (IO_OK==(res=iload->OpenChunk())) {
		switch(iload->CurChunkID())  {
		case VERSION_CHUNKID:
				iload->Read (&version, sizeof(version), &nb);
				break;
		case COLORLIST_CHUNKID:
				{
					iload->Read(&numColors,sizeof(int), &nb);
					d->AllocColorData(numColors);
					for (int i=0; i<numColors; i++) {
						iload->Read(&col.color,sizeof(col.color), &nb);
						d->SetColor(i, col);
					}
				}
				break;
			}
		iload->CloseChunk();
		if (res!=IO_OK) return res;
		}
	return IO_OK;
	}

void VertexPaint::PaletteButton(HWND hWnd)
	{
	IColorSwatch* iPal = GetIColorSwatch(hWnd);
	if (iPal && iColor) {
		iColor->SetColor(iPal->GetColor(), TRUE);
		}
	}

void VertexPaint::InitPalettes()
	{
	IColorSwatch* c;
	for (int i=0; i<NUMPALETTES; i++) {
		c = GetIColorSwatch(hPaletteWnd[i]);
		c->SetColor(palColors[i]);
		ReleaseIColorSwatch(c);
		}
	}

void VertexPaint::SavePalettes()
	{
	IColorSwatch* c;
	for (int i=0; i<NUMPALETTES; i++) {
		c = GetIColorSwatch(hPaletteWnd[i]);
		palColors[i] = c->GetColor();
		ReleaseIColorSwatch(c);
		}
	// Save Gradient Palettes.
	lastGradientColor[0]= iColorGradient[0]->GetColor();
	lastGradientColor[1]= iColorGradient[1]->GetColor();
	}

void VertexPaint::TurnVCOn(BOOL shaded)
{
	ModContextList list;
	INodeTab NodeTab;
	
	// Only the selected nodes will be affected
	ip->GetModContexts(list,NodeTab);

	for( int i = 0 ; i < NodeTab.Count() ; i++)
	{
		if(shaded)
			NodeTab[i]->SetShadeCVerts(!NodeTab[i]->GetShadeCVerts());
		else
			NodeTab[i]->SetCVertMode(!NodeTab[i]->GetCVertMode());	
		
	}
	NotifyDependents(FOREVER, PART_VERTCOLOR, REFMSG_CHANGE);
	ip->RedrawViews(ip->GetTime());
}


// *****************************************************************
void	VertexPaint::setEditionType(int editMode)
{
	if(editMode<0)	editMode= 0;
	if(editMode>2)	editMode= 2;

	// backup current Color according to editMode
	backupCurrentColor();

	_EditType= editMode;

	NotifyDependents(FOREVER, PART_VERTCOLOR, REFMSG_CHANGE);
	ip->RedrawViews(ip->GetTime());

	// Change Color Swatch according to editMode.
	IColorSwatch* c;
	for (int i=0; i<NUMPALETTES; i++) 
	{
		// Change palColors[i].
		int	val;
		if(editMode==0)
			val= i*255 / (4-1);		// 0, 85, 170, 255
		else
			val= (i*256+128) / 4;		// 32, 96, 160, 224
		// Change Addditional Palette colors.
		if(i>=4)
		{
			if(editMode==0)
				val= 42 + (i-4)*255 / (4-1);	// 42, 127, 212
			else
				val= 0;		// Phase not used
		}
		// Setup Color
		palColors[i]= RGB(val, val, val);


		c = GetIColorSwatch(hPaletteWnd[i]);
		c->SetColor(palColors[i]);
		ReleaseIColorSwatch(c);
	}

	// change current Color according to editMode
	reloadBkupColor();
}

// *****************************************************************
void	VertexPaint::backupCurrentColor()
{
	switch(getEditionType())
	{
	case 0: lastWeightColor = iColor->GetColor(); break;
	case 1:
	case 2: lastPhaseColor = iColor->GetColor(); break;
	}
}
void	VertexPaint::reloadBkupColor()
{
	// Change current color according to editMode.
	switch(getEditionType())
	{
	case 0: iColor->SetColor(lastWeightColor); break;
	case 1: 
	case 2: iColor->SetColor(lastPhaseColor); break;
	}
}


// *****************************************************************
void	VertexPaint::fillSelectionColor()
{
	int		mci;

	// Put Data in Undo/Redo List.
	if(!theHold.Holding())
		theHold.Begin();
	
	ModContextList	modContexts;
	INodeTab		nodeTab;
	
	GetCOREInterface()->GetModContexts(modContexts, nodeTab);
	
	for (mci=0; mci<modContexts.Count(); mci++) 
	{
		ModContext *mc = modContexts[mci];
		if(mc && mc->localData)
			theHold.Put(new VertexPaintRestore((VertexPaintData*)mc->localData, this));
	}

	theHold.Accept(GetString(IDS_RESTORE_FILL));


	// Which Component to change??
	VertexPaintData::TComponent	whichComponent;
	switch(getEditionType())
	{
	case 0: whichComponent= VertexPaintData::Red; break;
	case 1: whichComponent= VertexPaintData::Green; break;
	case 2: whichComponent= VertexPaintData::Blue; break;
	}


	// Modify all meshes.
	for (mci=0; mci<modContexts.Count(); mci++) 
	{
		ModContext *mc = modContexts[mci];
		if(mc && mc->localData)
		{
			VertexPaintData* d = (VertexPaintData*)mc->localData;
			Mesh*		mesh = 	d->GetMesh();
			if (mesh && mesh->vertCol) 
			{
				// For all faces of the mesh
				for(int fi=0; fi<mesh->getNumFaces(); fi++)
				{
					Face* f = &mesh->faces[fi];

					for (int i=0; i<3; i++) 
					{
						// Skip painting because not selected??
						if(mesh->selLevel == MESH_VERTEX && !mesh->VertSel()[f->v[i]] )
							continue;
						if(mesh->selLevel == MESH_FACE && !mesh->FaceSel()[fi])
							continue;
						// Also skip if face is hidden.
						if(f->Hidden())
							continue;

						// Apply painting
						d->SetColor(f->v[i],  1, GetActiveColor(), whichComponent);
					}
				}
			}
		}
	}

	// refresh
	NotifyDependents(FOREVER, PART_VERTCOLOR, REFMSG_CHANGE);
	ip->RedrawViews(ip->GetTime());
}

// *****************************************************************
void	VertexPaint::fillSelectionGradientColor()
{
	int		mci;

	// Put Data in Undo/Redo List.
	if(!theHold.Holding())
		theHold.Begin();
	
	ModContextList	modContexts;
	INodeTab		nodeTab;
	
	GetCOREInterface()->GetModContexts(modContexts, nodeTab);
	
	for (mci=0; mci<modContexts.Count(); mci++) 
	{
		ModContext *mc = modContexts[mci];
		if(mc && mc->localData)
			theHold.Put(new VertexPaintRestore((VertexPaintData*)mc->localData, this));
	}

	theHold.Accept(GetString(IDS_RESTORE_GRADIENT));


	// Which Component to change??
	VertexPaintData::TComponent	whichComponent;
	switch(getEditionType())
	{
	case 0: whichComponent= VertexPaintData::Red; break;
	case 1: whichComponent= VertexPaintData::Green; break;
	case 2: whichComponent= VertexPaintData::Blue; break;
	}
	COLORREF	grad0= iColorGradient[0]->GetColor();
	COLORREF	grad1= iColorGradient[1]->GetColor();


	// Get Matrix to viewport.
	Matrix3		viewMat;
	{
		ViewExp *ve = GetCOREInterface()->GetActiveViewport();
		// The affine TM transforms from world coords to view coords
		ve->GetAffineTM(viewMat);
		GetCOREInterface()->ReleaseViewport(ve);
	}


	// Modify all meshes.
	for (mci=0; mci<modContexts.Count(); mci++) 
	{
		ModContext *mc = modContexts[mci];
		if(mc && mc->localData)
		{
			VertexPaintData* d = (VertexPaintData*)mc->localData;
			Mesh*		mesh = 	d->GetMesh();
			if (mesh && mesh->vertCol) 
			{
				float	yMini= FLT_MAX;
				float	yMaxi= -FLT_MAX;

				// 1st, For all faces of the mesh, comute BBox of selection.
				int fi;
				for(fi=0; fi<mesh->getNumFaces(); fi++)
				{
					Face* f = &mesh->faces[fi];

					for (int i=0; i<3; i++) 
					{
						// Skip painting because not selected??
						if(mesh->selLevel == MESH_VERTEX && !mesh->VertSel()[f->v[i]] )
							continue;
						if(mesh->selLevel == MESH_FACE && !mesh->FaceSel()[fi])
							continue;
						// Also skip if face is hidden.
						if(f->Hidden())
							continue;

						// Transform to viewSpace.
						Point3	p= viewMat*mesh->getVert(f->v[i]);
						// extend bbox.
						yMini= p.y<yMini?p.y:yMini;
						yMaxi= p.y>yMaxi?p.y:yMaxi;
					}
				}

				// 2nd, For all faces of the mesh, fill with gradient
				for(fi=0; fi<mesh->getNumFaces(); fi++)
				{
					Face* f = &mesh->faces[fi];

					for (int i=0; i<3; i++) 
					{
						// Skip painting because not selected??
						if(mesh->selLevel == MESH_VERTEX && !mesh->VertSel()[f->v[i]] )
							continue;
						if(mesh->selLevel == MESH_FACE && !mesh->FaceSel()[fi])
							continue;
						// Also skip if face is hidden.
						if(f->Hidden())
							continue;

						// Compute gradientValue.
						float	gradValue;
						Point3	p= viewMat*mesh->getVert(f->v[i]);
						gradValue= (p.y-yMini)/(yMaxi-yMini);
						// Modifie with bendPower. 1->6.
						float	pow= 1 + fGradientBend * 5;
						gradValue= powf(gradValue, pow);

						// Apply painting
						// Reset To 0.
						d->SetColor(f->v[i],  1, grad0, whichComponent);
						// Blend with gradientValue.
						d->SetColor(f->v[i],  gradValue, grad1, whichComponent);
					}
				}

			}
		}
	}

	// refresh
	NotifyDependents(FOREVER, PART_VERTCOLOR, REFMSG_CHANGE);
	ip->RedrawViews(ip->GetTime());
}


// *****************************************************************
VertexPaintData::VertexPaintData(Mesh& m) : mesh(NULL), colordata(NULL), nverts(NULL), 
nvcverts(NULL), numColors(0), numnverts(0), numnvcverts(0)
{
	SetCache(m);
}

VertexPaintData::VertexPaintData() : mesh(NULL), colordata(NULL), nverts(NULL), 
nvcverts(NULL), numColors(0), numnverts(0), numnvcverts(0)
{

}

VertexPaintData::~VertexPaintData()
	{
	FreeCache();

	if (colordata) delete [] colordata;
	if(nverts) delete [] nverts;
	if(nvcverts) delete [] nvcverts;

	nverts = NULL;
	nvcverts = NULL;
	colordata = NULL;
	
	numColors = 0;
	numnverts = 0;
	numnvcverts = 0;
	}

void VertexPaintData::SetCache(Mesh& m)
{
	FreeCache();
	mesh = new Mesh(m);
	SynchVerts(m);
	AllocColorData(mesh->getNumVerts());
				
}

void VertexPaintData::FreeCache()
	{
	if (mesh) delete mesh;
	if(nverts) delete [] nverts;			 
	if(nvcverts) delete [] nvcverts;

	mesh = NULL;
	nverts = NULL;
	nvcverts = NULL;
	numnverts = 0;
	numnvcverts = 0;
	}

Mesh* VertexPaintData::GetMesh()
	{
	return mesh;
	}

NVert&  VertexPaintData::GetNVert(int i)
{
	static NVert nv;
	
	if (numnverts > i)
		return nverts[i];
	else
		return nv;
}

NVert& VertexPaintData::GetNVCVert(int i)
{
	static NVert nv;
	
	if (numnvcverts > i)
		return nvcverts[i];
	else
		return nv;
}

COLORREF& VertexPaintData::GetColor(int i)
	{
	static COLORREF c = RGB(0,0,0);
	if (numColors > i)
		return colordata[i].color;
	else
		return c;
	}

ColorData& VertexPaintData::GetColorData(int i)
	{
	static ColorData c;

	if (numColors > i)
		return colordata[i];
	else
		return c;
	}

void VertexPaintData::SetColor(int i, float bary, COLORREF c, TComponent whichComp)
{
	
	if (colordata && numColors > i) 
	{
		// change color.
		COLORREF	oldColor= colordata[i].color;
		int			oldVal;
		int			editVal;
		int			newVal;
		
		// Mask good component.
		switch(whichComp)
		{
		case  Red:
			oldVal= GetRValue(colordata[i].color);
			editVal= GetRValue(c);
			break;
		case  Green:
			oldVal= GetGValue(colordata[i].color);
			editVal= GetGValue(c);
			break;
		case  Blue:
			oldVal= GetBValue(colordata[i].color);
			editVal= GetBValue(c);
			break;
		}

		// Blend Color component
		// This color was set before !
		float alpha = (1.0f-bary);
		
		// Compute new value
		newVal= (int)(alpha*oldVal + bary*editVal);

		// Mask good component.
		switch(whichComp)
		{
		case  Red:
			colordata[i].color= (RGB(newVal, 0, 0)) | (oldColor & RGB(0,255,255));
			break;
		case  Green:
			colordata[i].color= (RGB(0, newVal, 0)) | (oldColor & RGB(255,0,255));
			break;
		case  Blue:
			colordata[i].color= (RGB(0, 0, newVal)) | (oldColor & RGB(255,255,0));
			break;
		}

	}
}

void VertexPaintData::SetColor(int i, const ColorData &c)
{
	if (colordata && numColors > i) 
	{
		colordata[i]= c;
	}
}

int VertexPaintData::GetNumColors()
	{
	return numColors;
	}

void VertexPaintData::AllocColorData(int numcols)
	{
	ColorData* newColorData;

	// Colors already exist.
	if (numColors == numcols)
		return;
	
	if (numColors > 0 ) 
	{
		// If the new number of colors is bigger than what we have in the colordata array
		if(numcols > numColors)
		{
			// Allocate a new color list and fill in as many as
			// we have from the previous set
			newColorData = new ColorData[numcols];
			
			for (int i=0; i<numcols; i++) 
			{
				if (i < numColors) 
				{
					newColorData[i] = colordata[i];
				}
			}
			delete [] colordata;
			
			colordata = newColorData;
			
			numColors = numcols;
			
		}
		else
		{
			numColors = numcols;
		}
	}
	else
	{
		// Allocate a complete new set of colors
		numColors = numcols;
		colordata = new ColorData[numColors];
	}
}

LocalModData* VertexPaintData::Clone()
	{
	VertexPaintData* d = new VertexPaintData();

	if (colordata) {
		d->colordata = new ColorData[numColors];
		d->numColors = numColors;
		for (int i=0; i<numColors; i++) {
			d->colordata[i] = colordata[i];
			}
		}
	if(nverts)
	{
		d->nverts = new NVert[numnverts];
		for(int i = 0 ; i < numnverts ; i++ ) {
			d->nverts[i] = nverts[i];
		}

	}
	if(nvcverts)
	{
		d->nvcverts = new NVert[numnvcverts];
		for(int i = 0 ; i < numnvcverts ; i++ ) {
			d->nvcverts[i] = nvcverts[i];
		}

	}

	return d;
	}


void VertexPaintData::SynchVerts(Mesh &m)
{
	if (mesh == NULL)
	{
		nlwarning("mesh == NULL");
		return;
	}

	if(nverts)
		delete [] nverts;
	
	numnverts = m.getNumVerts();
	
	nverts = new NVert[numnverts];

	if(nvcverts)
		delete [] nvcverts;
	
	numnvcverts = m.getNumVertCol();

	nvcverts = new NVert[numnvcverts];
	
	for(int i = 0 ; i < mesh->getNumFaces() ; i++)
	{	
		// for each vertex of each face
		for(int j = 0 ; j < 3 ; j++)
		{		
			int iCur = nverts[mesh->faces[i].v[j]].faces.Count();
			
			// Tell the vertex, which to which face it belongs and which 
			// of the three face v-indices corresponds to the vertex
			
			nverts[mesh->faces[i].v[j]].faces.SetCount(iCur+1);
			nverts[mesh->faces[i].v[j]].whichVertex.SetCount(iCur+1);

			nverts[mesh->faces[i].v[j]].faces[iCur] = i;
			nverts[mesh->faces[i].v[j]].whichVertex[iCur] = j;
			
			
			if(mesh->vcFace)
			{
				// Do the same for texture vertices
			iCur = nvcverts[mesh->vcFace[i].t[j]].faces.Count();
			
			nvcverts[mesh->vcFace[i].t[j]].faces.SetCount(iCur+1);
			nvcverts[mesh->vcFace[i].t[j]].whichVertex.SetCount(iCur+1);
			
			nvcverts[mesh->vcFace[i].t[j]].faces[iCur] = i;
			nvcverts[mesh->vcFace[i].t[j]].whichVertex[iCur] = j;

			}
			else
				nlassert(0);
		}
	}
}


//***************************************************************************
//**
//** NVert
//**
//***************************************************************************



NVert::NVert()
{
	faces.SetCount(0);
	whichVertex.SetCount(0);
}

NVert& NVert::operator= (NVert &nvert)
{
	faces = nvert.faces;
	whichVertex = nvert.whichVertex;
	return *this;
}

//***************************************************************************
//**
//** ColorData 
//**
//***************************************************************************


ColorData::ColorData(DWORD col) : color(col)
{
}

ColorData::ColorData() : color(0)
{
}

//***************************************************************************
//**
//** VertexPaintRestore : public RestoreObj
//**
//***************************************************************************

VertexPaintRestore::VertexPaintRestore(VertexPaintData *pLocalData, VertexPaint *pVPaint) 
: pMod(pVPaint), pPaintData(pLocalData), redoColordata(NULL)
{
	colordata = new ColorData[pPaintData->numColors];
	for(int i = 0; i < pPaintData->numColors ; i++)
	{
		colordata[i] = pPaintData->colordata[i];
	}
	numcolors = pPaintData->numColors;

}

VertexPaintRestore::~VertexPaintRestore()
{
	if(colordata)
		delete [] colordata;
	
	if(redoColordata)
		delete [] redoColordata;
}

void VertexPaintRestore::Restore(int isUndo)
{
	if(isUndo)
	{
		nlassert(pPaintData->colordata);

		redoColordata = pPaintData->colordata;
		redonumcolors = pPaintData->numColors;

		pPaintData->colordata = colordata;
		pPaintData->numColors = numcolors;

		colordata = NULL;
		
		pMod->NotifyDependents(FOREVER, PART_VERTCOLOR, REFMSG_CHANGE);
		GetCOREInterface()->RedrawViews(GetCOREInterface()->GetTime());
	}
}

void VertexPaintRestore::Redo()
{
	nlassert(pPaintData->colordata);
	
	colordata = pPaintData->colordata;
	numcolors = pPaintData->numColors;
	
	pPaintData->colordata = redoColordata;
	pPaintData->numColors = redonumcolors;
	
	redoColordata = NULL;

	pMod->NotifyDependents(FOREVER, PART_VERTCOLOR, REFMSG_CHANGE);
	GetCOREInterface()->RedrawViews(GetCOREInterface()->GetTime());
	
}

int  VertexPaintRestore::Size()
{
	int iSize = 0;
	
	if(colordata)
		iSize += sizeof(ColorData) * numcolors;
	
	if(redoColordata)
		iSize += sizeof(ColorData) * redonumcolors;

	return iSize;
}

